﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using DDD.Core;

namespace DDD.OpenTK {
    /// <summary>
    /// <see cref="VertexBuffer"/>クラスに拡張メソッドを追加するクラス
    /// </summary>
    public static class VertexBufferExtensions {

        /// <summary>
        /// コンパイルします
        /// </summary>
        /// <remarks>
        /// シェーダーで使われていないAttribute変数は無視します。
        /// </remarks>
        /// <param name="vbuf"><see cref="VertexBuffer"/>オブジェクト</param>
        /// <param name="prog"><see cref="ShaderProgram"/>オブジェクト</param>
        public static void Compile (this VertexBuffer vbuf, ShaderProgram prog) {
            var cache = Graphics3DExtensions.GetGLCache ();
            var id = cache[vbuf];
            if (id > 0) {
                return;
            }
            ErrorCode err;

            GL.GenVertexArrays (1, out id);
            if ((err = GL.GetError ()) > 0) {
                throw new InvalidOperationException ("GenVertexArrays is failed");
            }

            GL.BindVertexArray (id);

            // Attribute変数の設定
            foreach (var attr in vbuf.Attributes) {
                var query = (from x in prog.Attributes
                             where x.Name == attr.Name
                             select new { Location = x.Location }).FirstOrDefault ();
                if (query == null) {
                    // 見つからなかったら無視
                    continue;
                }
                var loc = query.Location;

                var varry = attr.VertexArray;
                var count = varry.ComponentCount;
                var type = varry.ComponentType;

                varry.Compile ();

                GL.EnableVertexAttribArray (loc);
                if (GL.GetError () > 0) {
                    throw new InvalidOperationException ("glEnableVertexAttribArray was failed, err=" + err);
                }

                GL.BindBuffer (BufferTarget.ArrayBuffer, cache[varry]);

                switch (type) {
                    case ComponentType.Byte: GL.VertexAttribIPointer (loc, count, VertexAttribIPointerType.UnsignedByte, 0, (IntPtr)0); break;
                    case ComponentType.SByte: GL.VertexAttribIPointer (loc, count, VertexAttribIPointerType.Byte, 0, (IntPtr)0); break;
                    case ComponentType.Short: GL.VertexAttribIPointer (loc, count, VertexAttribIPointerType.Short, 0, (IntPtr)0); break;
                    case ComponentType.UShort: GL.VertexAttribIPointer (loc, count, VertexAttribIPointerType.UnsignedShort, 0, (IntPtr)0); break;
                    case ComponentType.Int: GL.VertexAttribIPointer (loc, count, VertexAttribIPointerType.Int, 0, (IntPtr)0); break;
                    case ComponentType.UInt: GL.VertexAttribIPointer (loc, count, VertexAttribIPointerType.UnsignedInt, 0, (IntPtr)0); break;
                    case ComponentType.Float: GL.VertexAttribPointer (loc, count, VertexAttribPointerType.Float, false, 0, (IntPtr)0); break;
                    default: throw new NotImplementedException ("Sorry");
                }
                if (GL.GetError () > 0) {
                    throw new InvalidOperationException ("glVertexAttribPointer was failed, err=" + err);
                }
            }

            GL.BindVertexArray (0);
            cache.Add (vbuf, id);
        }

        /// <summary>
        /// レンダリング
        /// </summary>
        /// <param name="vbuf"><see cref="VertexBuffer"/>オブジェクト</param>
        public static void Render (this VertexBuffer vbuf) {
            var cache = Graphics3DExtensions.GetGLCache ();
            var id = cache[vbuf];
            if (id == 0) {
                throw new InvalidOperationException ("OpenGL id was not found");
            }

            GL.BindVertexArray (id);
        }
    }
}
